/***
 * Copyright (c) 2021-2031 murenchao
 * fig is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.software.fig.libs

import java.util.Collection
import java.util.List
import java.util.Map
import java.util.Set
import java.util.concurrent.ConcurrentHashMap
import org.apache.commons.lang3.builder.ToStringBuilder
import org.eclipse.xtend.lib.annotations.Accessors
import java.io.Serializable
import org.apache.commons.lang3.SerializationUtils

@Accessors
class Series implements Serializable{
	
	Set<Object> index = null
	Collection<Object> values = null
	ConcurrentHashMap<Object, Object> results = new ConcurrentHashMap<Object, Object>()
	public final static String NaN = "NaN"

	def static void main(String[] args) {
		var a = new Series(newArrayList(1, 2, 3, 4));
		println(a)
		var s = new Series(newArrayList(1, 2, 3, 4), newArrayList("a", "b", "c", "d"));
		println(s)
		var b = new Series(newArrayList(1, -2, 3, 4,1.9,"字符"), newArrayList("a", "b", "c", "d","e","字符"));
		println((s * b).toInt())
	}

	new(List<?> data) {
		this(data, null)
	}

	new(Map<Object, Object> data) {
		this(data, null)
	}

	new(Map<Object, Object> data, List<?> index) {
		if (index === null) {
			data.forEach [ k, v |
				results.put(k, v)
			]
		} else {
			for (i : 0 .. index.size - 1) {
				var key = index.get(i)
				if (data.containsKey(key)) {
					results.put(key, data.get(key))
				} else {
					results.put(key, NaN)
				}
			}
		}
		index = results.keySet
		values = results.values
	}

	new(List<?> data, List<?> index) {
		if (index === null) {
			for (i : 0 .. data.size - 1) {
				results.put(i, data.get(i))
			}
		} else {
			for (i : 0 .. index.size - 1) {
				if(i < data.size){
					results.put(index.get(i), data.get(i))
				}else{
					results.put(index.get(i),NaN)
				}
			}
		}
		index = results.keySet
		values = results.values
	}

	override toString() {
		return new ToStringBuilder(this).append(index).append(values).build()
	}
	
	def get(Object key){
		return this.results.get(key)
	}
	
	def toInt(){
		var newResult = SerializationUtils.clone(this.results)
		for(k : newResult.keySet){
			var v = newResult.get(k)
			if(!v.equals(NaN))
				newResult.put(k,(v as Number).intValue)
		}
		return new Series(newResult)
	}
	
	def toDouble(){
		var newResult = SerializationUtils.clone(this.results)
		for(k : newResult.keySet){
			var v = newResult.get(k)
			if(!v.equals(NaN))
				newResult.put(k,(v as Number).doubleValue)
		}
		return new Series(newResult)
	}
	
	def toLong(){
		var newResult = SerializationUtils.clone(this.results)
		for(k : newResult.keySet){
			var v = newResult.get(k)
			if(!v.equals(NaN))
				newResult.put(k,(v as Number).longValue)
		}
		return new Series(newResult)
	}
	
	def toFloat(){
		var newResult = SerializationUtils.clone(this.results)
		for(k : newResult.keySet){
			var v = newResult.get(k)
			if(!v.equals(NaN))
				newResult.put(k,(v as Number).floatValue)
		}
		return new Series(newResult)
	}
	
	def -(Series ser) {
		val result = new ConcurrentHashMap<Object, Object>()
		this.results.forEach [ k, v |
			if (ser.results.containsKey(k)) {
				var left = v;
				var right = ser.results.get(k);
				if (left.equals(NaN)) {
					result.put(k, NaN)
				} else if (right.equals(NaN)) {
					result.put(k, NaN)
				} else if(left instanceof Number && right instanceof Number){
					result.put(k, (left as Number).doubleValue - (right as Number).doubleValue)
				}else{
					result.put(k,NaN)
				}
			} else {
				result.put(k, NaN)
			}
		]
		ser.results.forEach [ k, v |
			if (!result.containsKey(k)) {
				result.put(k, NaN)
			}
		]
		return new Series(result)
	}

	def /(Series ser) {
		val result = new ConcurrentHashMap<Object, Object>()
		this.results.forEach [ k, v |
			if (ser.results.containsKey(k)) {
				var left = v;
				var right = ser.results.get(k);
				if (left.equals(NaN)) {
					result.put(k, NaN)
				} else if (right.equals(NaN)) {
					result.put(k, NaN)
				} else {
					result.put(k, (left as Number).doubleValue / (right as Number).doubleValue)
				}
			} else {
				result.put(k, NaN)
			}
		]
		ser.results.forEach [ k, v |
			if (!result.containsKey(k)) {
				result.put(k, NaN)
			}
		]
		return new Series(result)
	}

	def *(Series ser) {
		val result = new ConcurrentHashMap<Object, Object>()
		this.results.forEach [ k, v |
			if (ser.results.containsKey(k)) {
				var left = v;
				var right = ser.results.get(k);
				if (left.equals(NaN)) {
					result.put(k, NaN)
				} else if (right.equals(NaN)) {
					result.put(k, NaN)
				} else {
					result.put(k, (left as Number).doubleValue * (right as Number).doubleValue)
				}
			} else {
				result.put(k, NaN)
			}
		]
		ser.results.forEach [ k, v |
			if (!result.containsKey(k)) {
				result.put(k, NaN)
			}
		]
		return new Series(result)
	}

	def +(Series ser) {
		val result = new ConcurrentHashMap<Object, Object>()
		this.results.forEach [ k, v |
			if (ser.results.containsKey(k)) {
				var left = v;
				var right = ser.results.get(k);
				if (left.equals(NaN)) {
					result.put(k, NaN)
				} else if (right.equals(NaN)) {
					result.put(k, NaN)
				} else {
					result.put(k, (left as Number).doubleValue + (right as Number).doubleValue)
				}
			} else {
				result.put(k, NaN)
			}
		]
		ser.results.forEach [ k, v |
			if (!result.containsKey(k)) {
				result.put(k, NaN)
			}
		]
		return new Series(result)
	}
}
